package com.sy.process.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sy.auth.service.SysUserService;
import com.sy.model.process.Process;
import com.sy.model.process.ProcessRecord;
import com.sy.model.process.ProcessTemplate;
import com.sy.model.system.SysUser;
import com.sy.process.mapper.ProcessMapper;
import com.sy.process.service.ProcessRecordService;
import com.sy.process.service.ProcessService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sy.process.service.ProcessTemplateService;
import com.sy.security.custom.LoginUserInfoHelper;
import com.sy.vo.process.ApprovalVo;
import com.sy.vo.process.ProcessFormVo;
import com.sy.vo.process.ProcessQueryVo;
import com.sy.vo.process.ProcessVo;
import com.sy.wechat.service.MessageService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.task.Task;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.w3c.dom.ls.LSInput;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * <p>
 * 审批类型 服务实现类
 * </p>
 *
 * @author sy
 * @since 2023-04-30
 */
@Service
public class ProcessServiceImp extends ServiceImpl<ProcessMapper, Process> implements ProcessService {

    @Autowired
    RepositoryService repositoryService;
    @Autowired
    SysUserService sysUserService;
    @Autowired
    ProcessTemplateService processTemplateService;
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    TaskService taskService;
    @Autowired
    ProcessRecordService processRecordService;
    @Autowired
    HistoryService historyService;
    @Autowired
    MessageService messageService;

    @Override
    public IPage<ProcessVo> selectPage(Page<ProcessVo> pageParam, ProcessQueryVo processQueryVo) {
        IPage<ProcessVo> pageModel = baseMapper.selectPage(pageParam,processQueryVo);
        return pageModel;
    }

    @Override
    public void deployByZip(String deployPath) {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(deployPath);
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);

        // 部署
        repositoryService.createDeployment().addZipInputStream(zipInputStream).deploy();

    }

    // 发布流程实例
    @Override
    public void startUp(ProcessFormVo processFormVo) {
        // 1. 获得用户
        SysUser sysUser = sysUserService.getById(LoginUserInfoHelper.getUserId());

        // 2. 保存表单信息到业务表
        // 获得审批模板
        ProcessTemplate processTemplate = processTemplateService.getById(processFormVo.getProcessTemplateId());
        Process process = new Process();
        // 使用工具复制相同属性
        BeanUtils.copyProperties(processFormVo, process);
        // 其他值一个一个set
        process.setStatus(1);   // 审批中
        String workNo = System.currentTimeMillis() + "";
        process.setProcessCode(workNo);
        process.setUserId(LoginUserInfoHelper.getUserId());
        process.setFormValues(processFormVo.getFormValues());
        process.setTitle(sysUser.getName() + "发起" + processTemplate.getName() + "申请");
        // 保存信息
        baseMapper.insert(process);

        // 3. 启动流程实例
        // 3.1 流程定义key，直接从processTemplate中获得
        String processDefinitionKey = processTemplate.getProcessDefinitionKey();
        // 3.2 ProcessId
        String businessKey = String.valueOf(process.getId());
        // 3.3 流程参数，需要map集合
        String formValues = processFormVo.getFormValues();
        JSONObject formData = JSONObject.parseObject(formValues);
        Map<String, Object> map = new HashMap<>();
        for (Map.Entry<String, Object> entry : formData.entrySet()){
            map.put(entry.getKey(),entry.getValue());
        }
        Map<String, Object> variables = new HashMap<>();
        variables.put("data", map);
        // 3.4 启动
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
        process.setProcessInstanceId(processInstance.getId());

        // 4. 给审批人推送消息
        // 任务清单
        List<Task> taskList = this.getCurrentTaskList(processInstance.getId());
        if (!CollectionUtils.isEmpty(taskList)) {
            // 审批人清单
            List<String> assigneeList = new ArrayList<>();
            for (Task task : taskList) {
                String assigneeName = task.getAssignee();
                SysUser user = sysUserService.getUserByUserName(assigneeName);
                String name = user.getName();
                assigneeList.add(name);
                // 推送消息
                messageService.pushPendingMessage(process.getId(), sysUser.getId(), task.getId());

            }
            process.setDescription("等待" + StringUtils.join(assigneeList.toArray(), ",") + "审批");
        }
        // 5. 更新信息
        baseMapper.updateById(process);

        // 6. 保存记录
        processRecordService.record(process.getId(),1, "发起申请");

    }

    // 查询待处理的任务列表
    public IPage<ProcessVo> findPending(Page<Process> pageParam) {
        // 1. 根据审批人封装查询条件
        TaskQuery query = taskService.createTaskQuery().taskAssignee(LoginUserInfoHelper.getUsername())
                .orderByTaskCreateTime().desc();

        // 2. 分页条件查询代办
        // 开始位置
        int begin = (int)((pageParam.getCurrent() - 1) * pageParam.getSize());
        // 每页显示数
        int size = (int)pageParam.getSize();
        List<Task> tasks = query.listPage(begin, size);
        long totalCount = query.count();

        // 3. List<Task> --> List<ProcessVo>
        // task--> processInstance--> process--> processVo
        List<ProcessVo> processList = new ArrayList<>();
        for (Task task : tasks){
            // 获得实例id
            String processInstanceId = task.getProcessInstanceId();
            // 获得实例对象
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            // 获得业务Id
            String businessKey = processInstance.getBusinessKey();
            if (businessKey == null) continue;
            // 获得process对象
            Process process = this.getById(Long.parseLong(businessKey));
            // 创建processVo对象
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process, processVo);
            processVo.setTaskId(task.getId());
            processList.add(processVo);
        }

        // 4. 返回数据
        IPage<ProcessVo> page = new Page<>(pageParam.getCurrent(), pageParam.getSize(), totalCount);
        page.setRecords(processList);
        return page;
    }

    @Override
    public Map<String, Object> show(Long id) {
        // 1. 根据流程id获得流程信息
        Process process = baseMapper.selectById(id);
        // 2. 根据流程id获得流程记录信息
        List<ProcessRecord> processRecordList = processRecordService
                .list(new LambdaQueryWrapper<ProcessRecord>().eq(ProcessRecord::getProcessId, id));
        // 3. 根据流程id获得模板信息
        ProcessTemplate processTemplate = processTemplateService.getById(process.getProcessTemplateId());
        // 4. 判断当前用户是否可以审批
        // 不能重复审批，遍历任务，查看任务的审批人是不是当前用户
        boolean isApprove = false;
        List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
        if (!CollectionUtils.isEmpty(taskList)) {
            for(Task task : taskList) {
                if(task.getAssignee().equals(LoginUserInfoHelper.getUsername())) {
                    isApprove = true;
                }
            }
        }
        Map<String, Object> map = new HashMap<>();
        map.put("process", process);
        map.put("processRecordList", processRecordList);
        map.put("processTemplate", processTemplate);
        map.put("isApprove",isApprove);
        return map;
    }

    @Override
    public void approve(ApprovalVo approvalVo) {
        // 1. 获得task参数
        String taskId = approvalVo.getTaskId();
        Map<String, Object> variables1 = taskService.getVariables(taskId);
        for (Map.Entry<String, Object> entry : variables1.entrySet()){
            System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
        }

        // 2. 根据status的值进行不同的操作
        if (approvalVo.getStatus() == 1) {
            //已通过
            Map<String, Object> variables = new HashMap<String, Object>(); //流程变量
            taskService.complete(taskId, variables);
        } else {
            //驳回
            this.endTask(taskId);
        }

        // 3. 记录过程
        String description = approvalVo.getStatus().intValue() == 1 ? "已通过" : "驳回";
        processRecordService.record(approvalVo.getProcessId(), approvalVo.getStatus(), description);

        // 4. 查询下一个审批人
        // 并给下一个审批人进行消息推送
        Process process = this.getById(approvalVo.getProcessId());
        List<Task> currentTaskList = this.getCurrentTaskList(process.getProcessInstanceId());
        if (!CollectionUtils.isEmpty(currentTaskList)){
            List<String> assigneeList = new ArrayList<>();
            for (Task task : currentTaskList){
                SysUser sysUser = sysUserService.getUserByUserName(task.getAssignee());
                assigneeList.add(sysUser.getName());

                // 公众号推送消息
                messageService.pushPendingMessage(process.getId(), sysUser.getId(), task.getId());

            }
            // 更新流程信息
            process.setDescription("等待" + StringUtils.join(assigneeList.toArray(), ",") + "审批");
            process.setStatus(1);
        } else {
           // 没有下一个审批人
            if(approvalVo.getStatus().intValue() == 1) {
                process.setDescription("审批完成（同意）");
                process.setStatus(2);
            } else {
                process.setDescription("审批完成（拒绝）");
                process.setStatus(-1);
            }
        }
        // 更新process
        this.updateById(process);
        // 推送消息给申请人
        messageService.pushProcessedMessage(approvalVo, process.getUserId());
    }

    // 已处理
    @Override
    public IPage<ProcessVo> findProcessed(Page<Process> pageParam) {
        // 1. 查询历史记录，注意是历史任务
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(LoginUserInfoHelper.getUsername())
                .finished()
                .orderByTaskCreateTime().desc();
        int begin = (int) ((pageParam.getCurrent()-1)*pageParam.getSize());
        int size = (int) pageParam.getSize();
        List<HistoricTaskInstance> historicTaskInstances = query.listPage(begin, size);
        long totalCount = query.count();

        // 2. 封装成processVo返回
        List<ProcessVo> processVoList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(historicTaskInstances)){
            for (HistoricTaskInstance h : historicTaskInstances){
                ProcessVo processVo = new ProcessVo();
                Process process = this.getOne(new LambdaQueryWrapper<Process>()
                        .eq(Process::getProcessInstanceId, h.getProcessInstanceId()));
                if (process==null) continue;
                BeanUtils.copyProperties(process,processVo);
                processVo.setTaskId("0");
                processVoList.add(processVo);
            }
        }

        IPage<ProcessVo> page = new Page<>(pageParam.getCurrent(), pageParam.getSize(), totalCount);
        page.setRecords(processVoList);
        return page;
    }

    // 已发起
    @Override
    public IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam) {
        ProcessQueryVo processQueryVo = new ProcessQueryVo();
        processQueryVo.setUserId(LoginUserInfoHelper.getUserId());
        IPage<ProcessVo> page = baseMapper.selectPage(pageParam, processQueryVo);
        for (ProcessVo item : page.getRecords()) {
            item.setTaskId("0");
        }
        return page;
    }

    private void endTask(String taskId) {
        // 1. 根据任务id获得当前任务
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        // 2. 获得流程定义的模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());

        // 3. 获取结束流向节点
        List<EndEvent> endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
        if (CollectionUtils.isEmpty(endEventList)) return;
        FlowNode endFlowNode = endEventList.get(0);

        // 4. 获得当前节点
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());

        // 临时保存原来的流动方向
        List originalSequenceFlowList = new ArrayList<>();
        originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
        // 5. 清除当前流动方向
        currentFlowNode.getOutgoingFlows().clear();

        // 6. 创建一个新的流向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlow");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(endFlowNode);

        // 7. 当前节点指向新方向
        // 只是这里没有并行业务，参数是list
        List newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        // 8. 结束任务
        taskService.complete(taskId);

    }

    /**
     * 获取当前任务列表
     * @param processInstanceId
     * @return
     */
    private List<Task> getCurrentTaskList(String processInstanceId) {
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        return tasks;
    }
}
